#include <fstream>
#include <sstream>
#include <map>

#include "PlayerObject.h"
#include "PlayerObjectManager.h"

const char* const SOBSTRACLE_DEF_NAME = " table";
const char* const DOBSTRACLE_DEF_NAME = " position";
const char* const OBJECT_CLASS_NAME = " model";
const char* const OBJECT_SIZE_DEF = "size";
const char* const OBJECT_DEF_END = ")";


PlayerObjectManager::PlayerObjectManager() :
m_vecDynamicObjects(),
m_vecStaticObjects()
{
}


PlayerObjectManager::~PlayerObjectManager()
{
}

std::string PlayerObjectManager::ParseObjectName( const std::string& strObName, std::string& strLine )
{
    static const std::string strDefine = "define ";
    static const std::string strSpace = " ";
    static const std::string strTab = "\t";

    strLine.replace( strLine.find( strDefine ), strDefine.length(), "" );
    strLine.replace( strLine.find( strObName ), strObName.length(), "" );
    while ( std::string::npos != strLine.find( strSpace ))
    {
        strLine.replace( strLine.find( strSpace ), strSpace.length(), "" );
    }
    while ( std::string::npos != strLine.find( strTab ))
    {
        strLine.replace( strLine.find( strTab ), strTab.length(), "" );
    }

    return strLine.c_str();
}

void PlayerObjectManager::ParseObjectSize( std::string& strLine, double& dWidth, double& dBreadth, double& dHeight )
{
    static const std::string strLeft = "[";
    static const std::string strRight = "]";
    static const std::string strSize = OBJECT_SIZE_DEF;

    strLine.replace( strLine.find( strLeft ), strLeft.length(), "" );
    strLine.replace( strLine.find( strRight ), strRight.length(), "" );
    strLine.replace( strLine.find( strSize ), strSize.length(), "" );

    std::stringstream stmLineParser( strLine );
    stmLineParser >> dWidth >> dBreadth >> dHeight;
}

void PlayerObjectManager::ParseConfigFile(  const std::vector<std::string>& vecDefFiles, const std::string& strObjFile )
{
    std::string strLine = "";
    typedef std::map<std::string, PlayerObject> NameToObjMap; 
    NameToObjMap mapObjClass;

    for ( std::vector<std::string>::const_iterator itLoopDef = vecDefFiles.begin(); itLoopDef != vecDefFiles.end(); ++itLoopDef )
    {
        std::fstream fsConfigReader;
        fsConfigReader.open( itLoopDef->c_str(), std::ios_base::in );
        if ( !fsConfigReader.is_open() )
        {
            return;
        }

        std::string strCurObjectName = "";
        double dObWidth = 0.0;
        double dObHeight = 0.0;
        double dObBreadth = 0.0;
        bool bIsDynamic = false;
        bool bObjectClassBlock = false;

        while ( !fsConfigReader.eof() )
        {
            if ( fsConfigReader.bad() )
            {
                break;
            }

            std::getline( fsConfigReader, strLine );

            if ( strLine.empty() )
            {
                continue;
            }

            // Check and skip object class definition
            if ( std::string::npos != strLine.find( OBJECT_CLASS_NAME ))
            {
                bObjectClassBlock = true;
                continue;
            }

            // Check whether block end
            if ( std::string::npos != strLine.find( OBJECT_DEF_END ))
            {
                if ( bObjectClassBlock )
                {
                    bObjectClassBlock = false;
                    continue;
                }
                else
                {
                    PlayerObject oNewOb;
                    std::string strTempName = strCurObjectName + " ";
                    mapObjClass[strTempName] = oNewOb;
                    PlayerObject& refPushed = mapObjClass[strTempName];
                    refPushed.IsDynamic = bIsDynamic;
                    refPushed.Name = strCurObjectName.c_str();
                    refPushed.SizeX = dObWidth;
                    refPushed.SizeY = dObBreadth;
                    refPushed.SizeZ = dObHeight;
                }
            }

            // Skip when enter object class definition block
            if ( bObjectClassBlock )
            {
                continue;
            }

            if ( std::string::npos != strLine.find( SOBSTRACLE_DEF_NAME ))
            {
                strCurObjectName = ParseObjectName( SOBSTRACLE_DEF_NAME, strLine );
                bIsDynamic = false;
            }
            if ( std::string::npos != strLine.find( DOBSTRACLE_DEF_NAME ))
            {
                strCurObjectName = ParseObjectName( DOBSTRACLE_DEF_NAME, strLine );
                bIsDynamic = true;
            }

            if ( std::string::npos != strLine.find( OBJECT_SIZE_DEF ))
            {
                ParseObjectSize( strLine, dObWidth, dObBreadth, dObHeight );
            }
        }
    }

    std::fstream fsObjConfigReader;
    fsObjConfigReader.open( strObjFile.c_str(), std::ios_base::in );
    if ( !fsObjConfigReader.is_open() )
    {
        return;
    }

    while ( !fsObjConfigReader.eof() )
    {
        if ( fsObjConfigReader.bad() )
        {
            break;
        }

        std::getline( fsObjConfigReader, strLine );

        if ( strLine.empty() )
        {
            continue;
        }

        for ( NameToObjMap::iterator itLoop = mapObjClass.begin(); itLoop != mapObjClass.end(); ++itLoop )
        {
            if ( std::string::npos != strLine.find( itLoop->first ))
            {
                PlayerObject oNew;
                if ( itLoop->second.IsDynamic )
                {
                    m_vecDynamicObjects.push_back( oNew );
                }
                else
                {
                    m_vecStaticObjects.push_back( oNew );
                }
                PlayerObject& refNew = itLoop->second.IsDynamic ? m_vecDynamicObjects.back() : m_vecStaticObjects.back();

                refNew.IsDynamic = itLoop->second.IsDynamic;
                refNew.SizeX = itLoop->second.SizeX;
                refNew.SizeY = itLoop->second.SizeY;
                refNew.SizeZ = itLoop->second.SizeZ;

                std::string strContent = strLine.substr( itLoop->first.length() );
                ParseObject( strContent, refNew );

                break;
            }
        }
    }
}

void PlayerObjectManager::ParseObject( std::string& strLine, PlayerObject& oObj )
{
    size_t unName = strLine.find( "(name " );
    if ( std::string::npos == unName )
    {
        unName = strLine.find( " name " );
    }
    if ( std::string::npos != unName )
    {
        size_t unLeft = strLine.find( '\"', unName ) + 1u;
        size_t unRight = strLine.find( '\"', unLeft );
        oObj.Name = strLine.substr( unLeft, unRight - unLeft );
    }

    std::string strPose = "";
    size_t unPose = strLine.find( " pose " );
    if ( std::string::npos != unPose )
    {
        size_t unLeft = strLine.find( '[', unPose ) + 1u;
        size_t unRight = strLine.find( ']', unLeft );
        strPose = strLine.substr( unLeft, unRight - unLeft );

        std::stringstream stmLineParser( strPose );
        stmLineParser >> oObj.PosLowX >> oObj.PosHighY >> oObj.PosZ >> oObj.Yaw;
        oObj.updatePos( oObj.PosLowX, oObj.PosHighY, oObj.Yaw );
    }
}

std::vector<PlayerObject>& PlayerObjectManager::GetDynamicObjects()
{
    return m_vecDynamicObjects;
}

const std::vector<PlayerObject>& PlayerObjectManager::GetStaticObjects()
{
    return m_vecStaticObjects;
}

PlayerObjectManager& PlayerObjectManager::Instance()
{
    // No need to release during app process execution
    static PlayerObjectManager* pInstance = new PlayerObjectManager();

    return *pInstance;
}
